package com.zhss.microservice.server.network;

import com.zhss.microservice.common.entity.*;
import com.zhss.microservice.server.node.IController;
import com.zhss.microservice.server.node.Slot;
import com.zhss.microservice.server.node.SlotManager;
import com.zhss.microservice.server.registry.ServiceInstance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Map;

/**
 * 客户端请求处理组件
 */
public class ClientRequestProcessor {

    private static final Logger LOGGER = LoggerFactory.getLogger(ClientRequestProcessor.class);

    private static final Integer SLOT_COUNT = 16384;

    /**
     * Controller组件代理（controller或者controller候选人）
     */
    private IController controllerAgent;
    /**
     * 槽位管理组件
     */
    private SlotManager slotManager;

    /**
     * 构造函数
     * @param controllerAgent
     */
    public ClientRequestProcessor(
            IController controllerAgent,
            SlotManager slotManager) {
        this.controllerAgent = controllerAgent;
        this.slotManager = slotManager;
    }

    /**
     * 处理客户端请求
     * @param request
     */
    public Response process(Request request) {
        if(request instanceof FetchSlotsAllocationRequest) {
            return fetchSlotsAllocation((FetchSlotsAllocationRequest) request);
        } else if(request instanceof FetchServerAddressesRequest) {
            return fetchServerAddresses((FetchServerAddressesRequest) request);
        } else if(request instanceof RegisterRequest) {
            return register((RegisterRequest) request);
        } else if(request instanceof  HeartbeatRequest) {
            return heartbeat((HeartbeatRequest) request);
        }
        return null;
    }

    /**
     * 心跳
     * @param request
     * @return
     */
    private Response heartbeat(HeartbeatRequest request) {
        String serviceName = request.getServiceName();
        String serviceInstanceIp = request.getServiceInstanceIp();
        Integer serviceInstancePort = request.getServiceInstancePort();

        Integer slotNumber = routeSlot(serviceName);
        Slot slot = slotManager.getSlot(slotNumber);
        slot.heartbeat(serviceName, serviceInstanceIp, serviceInstancePort);

        LOGGER.info("完成服务实例【" + serviceName + "_" + serviceInstanceIp + "_" + serviceInstancePort + "】的心跳......");

        HeartbeatResponse.Builder builder = new HeartbeatResponse.Builder();
        HeartbeatResponse response = builder
                .requestId(request.getId())
                .result(RegisterResponse.SUCCESS)
                .build();

        return response;
    }

    /**
     * 服务注册
     * @param request
     * @return
     */
    private Response register(RegisterRequest request) {
        String serviceName = request.getServiceName();
        String serviceInstanceIp = request.getServiceInstanceIp();
        Integer serviceInstancePort = request.getServiceInstancePort();

        ServiceInstance serviceInstance = new ServiceInstance(
                serviceName,
                serviceInstanceIp,
                serviceInstancePort
        );

        Integer slotNumber = routeSlot(serviceName);
        Slot slot = slotManager.getSlot(slotNumber);
        slot.register(serviceInstance);

        LOGGER.info("完成服务实例【" + serviceInstance + "】的注册......");

        RegisterResponse.Builder builder = new RegisterResponse.Builder();
        RegisterResponse response = builder
                .requestId(request.getId())
                .result(RegisterResponse.SUCCESS)
                .build();

        return response;
    }

    /**
     * 将服务路由到slot
     * @param serviceName
     * @return
     */
    private Integer routeSlot(String serviceName) {
        int hashCode = serviceName.hashCode() & Integer.MAX_VALUE;
        Integer slot = hashCode % SLOT_COUNT;

        if(slot == 0) {
            slot = slot + 1;
        }

        return slot;
    }

    /**
     * 抓取slots分配数据
     * @param fetchSlotsAllocationRequest
     */
    private FetchSlotsAllocationResponse fetchSlotsAllocation(
            FetchSlotsAllocationRequest fetchSlotsAllocationRequest) {
        String requestId = fetchSlotsAllocationRequest.getId();
        Map<Integer, String> slotsAllocation = controllerAgent.getSlotsAllocation();

        FetchSlotsAllocationResponse.Builder builder =
                new FetchSlotsAllocationResponse.Builder();
        FetchSlotsAllocationResponse fetchSlotsAllocationResponse = builder
                .requestId(requestId)
                .slotsAllocation(slotsAllocation)
                .build();

        return fetchSlotsAllocationResponse;
    }

    /**
     * 抓取server地址列表
     * @param request
     */
    private FetchServerAddressesResponse fetchServerAddresses(
            FetchServerAddressesRequest request) {
        String requestId = request.getId();
        List<String> serverAddresses = controllerAgent.getServerAddresses();

        FetchServerAddressesResponse.Builder builder =
                new FetchServerAddressesResponse.Builder();
        FetchServerAddressesResponse response = builder
                .requestId(requestId)
                .serverAddresses(serverAddresses)
                .build();

        return response;
    }

}
